import Graphics from "./graphics/Graphics.js";
import Physics from "./physics/Physics.js";
import A3Object from "./A3Object.js";
import A3ObjectTypes from "./A3ObjectTypes.js";
import Stats from "./3rdParty/jsm/libs/stats.module.js";
import { GUI } from './3rdParty/jsm/libs/dat.gui.module.js';
import { saveString } from "./common/Common.js";
import A3Ground, { A3Ceiling, A3Door, A3Floor } from "./modules/static/A3Ground.js";
import { A3DirectionalLight } from "./graphics/A3Lights.js";
import { newA3Object } from "./A3ObjectGroup.js";
import { newA3Module } from "./modules/A3Module.js";

const params = {};
const actors = [];
const cameraMap = new Map();

/**
 * A3
 */
class A3{

  // 仿真控制
  isStop = true;
  isPause = false;

  // 导入导出控制
  autoClear = true;

  // 第一视角相机
  firstAngleCamera = undefined;
  firstAngleIsActive = false;

  // 第一视角按键映射
  cameraMovementKeyMap = {
    Forward: 'KeyW',
    Backward: 'KeyS',
    RotateLeft: 'KeyA',
    RotateRight: 'KeyD',
    LookUp:'KeyQ',
    LookDown:'KeyE',
    Jump:'Space',
  }

  constructor( mount ) {

    if( !mount ){
      mount = document.createElement('div');
    }    

    this.graphics = new Graphics( mount );
    this.physics = new Physics();
    this.mount = mount;
    this.domElement = mount;

    this.initInputFileForm();

    window.addEventListener( 'resize', this.onWindowResize.bind(this) );

    // 绑定键盘事件
    window.addEventListener( 'keydown', this.onKeyDown.bind(this) );
    window.addEventListener( 'keyup', this.onKeyUp.bind(this) );
    window.addEventListener( 'keypress', this.onKeyPress.bind(this) );

  }

/******* ******* ******* ******* ******* ******* *******/
/******* 事件处理 *******/
/******* ******* ******* ******* ******* ******* *******/
  onWindowResize() {
    this.graphics.resize();
  }

  onKeyDown( event ) {
 
    const key = event.code;
    const keyMap = this.cameraMovementKeyMap;

    // console.log( `${key} down...` );

    if( this.firstAngleCamera && this.firstAngleIsActive ) {
      switch(key) {
        case keyMap.Backward:
          this.firstAngleCamera.moveBackward( true, 1 );
          break;
        case keyMap.Forward:
          this.firstAngleCamera.doAction( "move_forward", true, 2 );
          break;
        case keyMap.RotateLeft:
          this.firstAngleCamera.rotateLeft();
          break;
        case keyMap.RotateRight:
          this.firstAngleCamera.rotateRight();
          break;
        case keyMap.LookUp:
          this.firstAngleCamera.lookUp();
          break;
        case keyMap.LookDown:
          this.firstAngleCamera.lookDown();
          break;
        case keyMap.Jump:
          this.firstAngleCamera.doJump();
          break;
      }
    }
  }

  onKeyUp( event ) {

    const key = event.code;
    const keyMap = this.cameraMovementKeyMap;

    if( this.firstAngleCamera && this.firstAngleIsActive ) {
      switch(key) {
        case keyMap.Backward:
          this.firstAngleCamera.moveBackward( false );
          break;
        case keyMap.Forward:
          this.firstAngleCamera.doAction( "move_forward", false );
          break;
        case keyMap.RotateLeft:
          this.firstAngleCamera.rotateLeft( false );
          break;
        case keyMap.RotateRight:
          this.firstAngleCamera.rotateRight( false );
          break;
        case keyMap.LookUp:
          this.firstAngleCamera.lookUp( false );
          break;
        case keyMap.LookDown:
          this.firstAngleCamera.lookDown( false );
          break;
        case keyMap.Jump:
          this.firstAngleCamera.doJump();
          break;
      }
    }

  }

  onKeyPress( event ) {

    const key = event.code;
    switch( key ){
      case "Backquote":
        for( var i = 0, il = actors.length; i < il; i++ ){
          actors[i].doAction("debug", Math.random());
        }
        break;
      case "KeyO":
        for( var i = 0, il = actors.length; i < il; i++ ) {
          if( actors[i].isOpenable ) {
            actors[i].doAction("open");
          }
        }
        break;
      case "KeyC":
        for( var i = 0, il = actors.length; i < il; i++ ) {
          if( actors[i].isOpenable ) {
            actors[i].doAction("close");
          }
        }
        break;
      case "KeyT":
        for( var i = 0, il = actors.length; i < il; i++ ) {
          if( actors[i].isOpenable ) {
            actors[i].doAction("stop", Math.PI/4);
          }
        }
        break;
    }

  }

/******* ******* ******* ******* ******* ******* *******/
/******* 初始化 *******/
/******* ******* ******* ******* ******* ******* *******/

  async init( size = 10 ) {

    await this.physics.init();
    this.graphics.init( size );
    this.animate();

    // this.initStats();
    // this.initGUI();

  }
  
  initStats() {

    const mount = this.mount;

    const stats = new Stats();
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.top = '0px';
    mount.appendChild( stats.domElement );

    this.stats = stats;

  }

  initGUI() {

    const gui = new GUI( params );

    const sim = gui.addFolder( '仿真控制' );
    
    params["start"] = this.start.bind(this);
    // params["resume"] = this.resume.bind(this);
    params["pause"] = this.pause.bind(this);
    params["reset"] = this.reset.bind(this);

    sim.add( params, "start" ).name("开始");
    // sim.add( params, "resume" ).name("继续");
    sim.add( params, "pause" ).name("暂停");
    sim.add( params, "reset" ).name("重置");

    // 导入/导出场景
    params["clear"] = this.clear.bind(this);
    params["export"] = this.export.bind(this);
    params["import"] = this.import.bind(this);
    params["list"] = this.listActors.bind(this);

    const sceneOpt = gui.addFolder("场景操作")
    sceneOpt.add( params, "clear").name("清空");
    sceneOpt.add( params, "list").name("列表");
    sceneOpt.add( params, "import").name("导入");
    sceneOpt.add( params, "export").name("导出");
    // sim.open();

    // 演员视图控制
    const view = gui.addFolder("显示视图");
    params["visual"] = true;
    params["thirdAngle"] = true;
    params["cameraList"] = ["default"];
    params["updateCameras"] = this.updateCameras.bind(this);
    params["cameraSelector"]= "default";

    const sw = view.add( params, "visual").name("视觉模型").onChange( val=>{
      for( var i = 0, il = actors.length; i < il; i++ ) {
        actors[i].switchView( val );
      }
      sw.name(val?"视觉模型":"碰撞模型");
    });

    // const angle = view.add( params, "thirdAngle").name("第三视角").onChange( val => {
    //   if( val ) {
    //     this.graphics.setActiveCamera();
    //     this.firstAngleIsActive = false;
    //   }
    //   else {
    //     this.firstAngleIsActive = this.activeFirstAngle();
    //   }
    //   angle.name( val ? "第三视角": "第一视角");
    // });

    view.add( params, "updateCameras").name("刷新相机");
    const cameraSelector = view.add( params, "cameraSelector" )
    .options(params["cameraList"])
    .name("相机选择")
    

    this.gui = gui;
    this.view = view;
    this.cameraSelector = cameraSelector;

  }

  // 导入文件控件
  initInputFileForm(){

    var form = document.createElement( 'form' );
    form.style.display = 'none';
    document.body.appendChild( form );

    var fileInput = document.createElement( 'input' );
    fileInput.multiple = false;
    fileInput.type = 'file';
    fileInput.addEventListener( 'change', () => {

      this.loadActors( fileInput.files[0] );
      form.reset();

    } );
    form.appendChild( fileInput );

    this.fileInput = fileInput;

  }

/******* ******* ******* ******* ******* ******* *******/
/******* 仿真控制 *******/
/******* ******* ******* ******* ******* ******* *******/

  start() {

    if( this.isPause ) {
      this.isPause = false;
    }

    if( this.isStop ) {

      this.isStop = false;
      this.physics.clear();

      for( var i = 0, il = actors.length; i < il; i++ ) {

        if( actors[i].isPhysical ) {
          this.physics.add( actors[i] );
          actors[i].store();
        }
        
      }

    }

    console.log("Start...");

  }

  pause() {
    
    this.isPause = true;
    console.log("Pause...");

  }

  resume() {

    this.isPause = false;
    console.log("Resume...");

  }

  reset() {
    
    this.isStop = true;

    for( var i = 0, il = actors.length; i < il; i++ ) {
      actors[i].reset();
    }

    console.log("Reset...");

  }

/******* ******* ******* ******* ******* ******* *******/
/******* 导入导出 *******/
/******* ******* ******* ******* ******* ******* *******/

  export() {
    const data = {};

    for( var i = 0, il = actors.length; i < il; i++ ) {
      // console.log( actors[i] );
      data[actors[i].uuid] = actors[i].toJson();
    }

    // console.log(data);

    saveString( JSON.stringify(data, null, '\n'), "scene.json");
  }

  import() {
    // 确保在重置状态导入
    if( !this.isStop ) {
      alert("导入前请先停止仿真.");
      return;
    }
  
    if( actors.length > 0 ) {
      this.autoClear = confirm("是否清空当前场景？");
    }
    
    this.fileInput.click();

  }

  loadActors( file ) {

    if( this.autoClear ) {
      this.clear();
    }

    if( file ) {
      const reader = new FileReader();
      reader.readAsText( file );
      reader.onload = ()=>{
        const json = JSON.parse( reader.result );
        for( var uuid in json ) {
          const isGroup = json[uuid].isA3ObjectGroup;
          let newActor = isGroup ? newA3Module( json[uuid]) : newA3Object( json[uuid] );
          if( newActor ) {
            this.add( newActor );
          }

        }
      }
    }

  }

  listActors() {

    const data = {};

    for( var i = 0, il = actors.length; i < il; i++ ) {
      // console.log( actors[i] );
      data[actors[i].uuid] = actors[i].toJson();
    }

    console.log(actors, data);

  }

/******* ******* ******* ******* ******* ******* *******/
/******* 更新 *******/
/******* ******* ******* ******* ******* ******* *******/

  update() {

    if( this.graphics ) this.graphics.update();
    if( this.physics && !this.isStop && !this.isPause ) this.physics.update();
    if( this.stats ) this.stats.update();

    for( var i = 0, il = actors.length; i < il; i++ ) {
      if( !actors[i].isPhysical ) {
        actors[i].update();
      }
      else if( !this.isStop && !this.isPause ) {
        actors[i].update();
      }
    }

  }

  animate() {

    requestAnimationFrame( this.animate.bind(this) );
    this.update();

  }

/******* ******* ******* ******* ******* ******* *******/
/******* 演员操作 *******/
/******* ******* ******* ******* ******* ******* *******/

  add( obj ) {

    this.graphics.add( obj );
    // this.physics.add( obj );

    if( obj.isA3Camera ) {
      this.firstAngleCamera = obj;
      cameraMap.set( obj.name, obj );
    }

    actors.push( obj );

  }

  clear() {
    
    this.graphics.clear();
    this.physics.clear();
    // todo: 清理缓存
    actors.length = 0;
    cameraMap.clear();

  }

  // activeFirstAngle() {

  //   if( this.firstAngleCamera ) {

  //     this.graphics.setActiveCamera( this.firstAngleCamera );
  //     return true;

  //   }
    
  //   return false;

  // }
  
  // setFirstAngleCamera( camera ) {

  //   this.firstAngleCamera = camera;

  //   // this.firstAngleIsActive = this.activeFirstAngle();

  // }

  updateCameras() {

    params["cameraList"].length = 0;
    params["cameraList"].push("default");

    cameraMap.forEach( (val, key)=>{
      params["cameraList"].push( key );
    } )   

    // update GUI
    this.view.remove( this.cameraSelector );

    this.cameraSelector = this.view.add( params, "cameraSelector")
    .options( params["cameraList"] )
    .name("相机选择")
    .onChange( (val)=>{
      
      const camera = cameraMap.get(val);
      this.graphics.setActiveCamera( camera );
      
      if( val !== "default" ) {
        this.firstAngleCamera = camera;
        this.firstAngleIsActive = true;
      }
      
    });

  }

}

A3.prototype.A3Object = A3Object;

export default A3;


